#include "YUVPboRender.h"
#include "base/YUVImage.h"
#include "utils/util.h"
#include <vcruntime.h>

namespace Sivin {
  YUVPBORender::YUVPBORender() {
    mVertexData = {
        //|---positon----|--texCoord--|
        {0.5f, 0.5f, 0.0f, 1.0f, 1.0f},//右上
        {-0.5, 0.5, 0.0f, 0.0f, 1.0f}, //左上
        {-0.5, -0.5, 0.0f, 0.0f, 0.0f},//左下
        {0.5, -0.5, 0.0f, 1.0f, 0.0f}};//右下

    mElementIndexArray = {
        0, 1, 2, // 第一个三角形
        0, 2, 3};// 第二个三角形

    mShader = std::make_unique<Shader>("./../shader/yuv/vert.vs", "./../shader/yuv/frag.fs");
  }

  YUVPBORender::~YUVPBORender() {
  }

  void YUVPBORender::onGLInit() {
    mShader->init();
    initVAO();
    initYUVPixelBuffer();

    mShader->start();
    mShader->setInt("yTexture", 0);
    mShader->setInt("uTexture", 1);
    mShader->setInt("vTexture", 2);

    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D, mYTextureId);

    glActiveTexture(GL_TEXTURE1);
    glBindTexture(GL_TEXTURE_2D, mUTextureId);

    glActiveTexture(GL_TEXTURE2);
    glBindTexture(GL_TEXTURE_2D, mVTextureId);

    //下面是4.5以上的api，不需要激活纹理单元，
    //注意参数应该填写的是0,1,2这种参数，而不是GL_TEXTURE0这种
    // glBindTextureUnit(0,mYTextureId);
    // glBindTextureUnit(1,mUTextureId);
    // glBindTextureUnit(2,mVTextureId);
  }

  void YUVPBORender::onRender() {
    mShader->start();
    glBindVertexArray(mVAO);
    glDrawElements(GL_TRIANGLES, mElementIndexArray.size(), GL_UNSIGNED_INT, (void *) 0);
    mShader->end();
  }

  void YUVPBORender::initVAO() {
    glGenVertexArrays(1, &mVAO);
    GLuint vbo, ebo;
    glGenBuffers(1, &vbo);
    glGenBuffers(1, &ebo);
    glBindVertexArray(mVAO);

    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(GLuint) * mElementIndexArray.size(),
                 mElementIndexArray.data(), GL_STATIC_DRAW);

    glBindBuffer(GL_ARRAY_BUFFER, vbo);
    glBufferData(GL_ARRAY_BUFFER, sizeof(SnVertex) * mVertexData.size(),
                 mVertexData.data(), GL_STATIC_DRAW);

    glEnableVertexAttribArray(0);
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(SnVertex), (void *) 0);
    glEnableVertexAttribArray(1);
    glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(SnVertex), (void *) offsetof(SnVertex, texCoord));

    glBindVertexArray(0);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
    glBindBuffer(GL_ARRAY_BUFFER, 0);
  }

  void YUVPBORender::initYUVPixelBuffer() {
    auto yuvImg = YUVImage::loadImage("./../res/image_640_480.yuv", 640, 480, YUVImage::Format::YUV420);

    glGenBuffers(1, &mYPboId);
    glBindBuffer(GL_PIXEL_UNPACK_BUFFER, mYPboId);
    size_t ySize = yuvImg->width() * yuvImg->height();
    //创建pbo内存空间
    glBufferData(GL_PIXEL_UNPACK_BUFFER, ySize, nullptr, GL_DYNAMIC_DRAW);
    void *ybuffer = glMapBufferRange(GL_PIXEL_UNPACK_BUFFER, 0, ySize, GL_MAP_WRITE_BIT | GL_MAP_INVALIDATE_RANGE_BIT);
    GLenum error = glGetError();
    if (error != GL_NO_ERROR) {
      printf("OpenGL error: %s\n", SnUtil::getGLError(error).c_str());
    }
    if (ybuffer) {
      memcpy(ybuffer, yuvImg->getYData(), ySize);
      glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER);
    } else {
      std::cout << "map pixel buffer failed\n";
    }

    mYTextureId = SnUtil::createTextureId();
    //glTexImage2D定义了一张纹理，内部会分配纹理空间，指定纹理的格式
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, yuvImg->width(), yuvImg->height(), 0, GL_RED, GL_UNSIGNED_BYTE, nullptr);
    glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, yuvImg->width(), yuvImg->height(), GL_RED, GL_UNSIGNED_BYTE, 0);

    //传递U通道数据
    glGenBuffers(1, &mUPboId);
    glBindBuffer(GL_PIXEL_UNPACK_BUFFER, mUPboId);
    size_t uvSize = ySize / 4;
    glBufferData(GL_PIXEL_UNPACK_BUFFER, uvSize, nullptr, GL_DYNAMIC_DRAW);
    void *ubuffer = glMapBufferRange(GL_PIXEL_UNPACK_BUFFER, 0, uvSize, GL_MAP_WRITE_BIT | GL_MAP_INVALIDATE_RANGE_BIT);
    if (ubuffer) {
      memcpy(ubuffer, yuvImg->getUData(), uvSize);
      glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER);
    }
    mUTextureId = SnUtil::createTextureId();
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, yuvImg->width() / 2, yuvImg->height() / 2, 0, GL_RED, GL_UNSIGNED_BYTE, nullptr);
    glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, yuvImg->width() / 2, yuvImg->height() / 2, GL_RED, GL_UNSIGNED_BYTE, 0);

    //传递V通道数据
    glGenBuffers(1, &mVPboId);
    glBindBuffer(GL_PIXEL_UNPACK_BUFFER, mVPboId);
    glBufferData(GL_PIXEL_UNPACK_BUFFER, uvSize, nullptr, GL_DYNAMIC_DRAW);
    void *vbuffer = glMapBufferRange(GL_PIXEL_UNPACK_BUFFER, 0, uvSize,  GL_MAP_WRITE_BIT | GL_MAP_INVALIDATE_RANGE_BIT);
    if (vbuffer) {
      memcpy(vbuffer, yuvImg->getVData(), uvSize);
      glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER);
    }

    mVTextureId = SnUtil::createTextureId();
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, yuvImg->width() / 2, yuvImg->height() / 2, 0, GL_RED, GL_UNSIGNED_BYTE, nullptr);
    glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, yuvImg->width() / 2, yuvImg->height() / 2, GL_RED, GL_UNSIGNED_BYTE, 0);
    glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
  }
}// namespace Sivin